서울시 치안데이터 분석

“CCTV가 많으면 범죄수를
낮추는 데에 도움이 될까요?”

출처: 서울시 공공데이터포털 (https://data.seoul.go.kr/)

데이터 살펴보기
TOP 3
    자치구       총생활인구수
16  송파구  753278.4966
0   강남구  633521.1274
3   강서구  533814.0426
TOP 3
    자치구   1인가구수
4   관악구  150745
3   강서구  104509
16  송파구   87140

데이터 살펴보기
자치구
강남구    7007
관악구    5366
서초구    5060
Name: count, dtype: int64
자치구
강남구    1713
용산구    1421
구로구    1372
Name: count, dtype: int64

안전벨과 CCTV의 상관관계: 0.3032
예상과 달리, CCTV와 안전벨의 상관관계가 높지 않다.

데이터 살펴보기


치안센터와 경찰관 수의 상관관계: 0.6500
다른 변수들과 비교했을 때 가장 높은 상관관계를 보임
종로구와 강남구만 조금 다르고 나머지는 비슷하다.

데이터 살펴보기
    자치구  술집 수
17  양천구  3094
7   금천구  3179
10  동작구  3276
     자치구  술집 수
12  서대문구  3578
7    금천구  3179
14   성동구  4001

술집 수와 총 범죄 건수의 상관계수: 0.8354
다른 변수들과 비교했을 때, 가장 높은 상관관계를 보임

상관계수 분석

범죄건수 주요 변수

  • 구별 경찰 수
  • 술집 수
  • 생활 인구 수
  • CCTV 총 수량
  • 1인가구수

회귀분석

상관 관계 높은 변수 임의 선택

VS Stepwise로 변수 선택

회귀분석

변수 전처리

df2 = df.copy()

scaler = StandardScaler()
x = ['총생활인구수', '총범죄건수', '구별 경찰수', 
    '안전벨 개수', 'CCTV 수량', '총 음식점 수', 
    '1인가구수', '파출소수']
X_scaled = scaler.fit_transform(df2[x])

X_scaled_df = pd.DataFrame(X_scaled, columns=x, index=df2.index)
  • 변수의 Scale 이 각각 다르기 때문에, 표준화 수행
  • 변수간 영향을 공정하게 비교하자

회귀분석(상관관계 기준)
X3 = X_scaled_df[['총 음식점 수','CCTV 수량','1인가구수','구별 경찰수','총생활인구수']]
y = X_scaled_df['총범죄건수']
X3 = sm.add_constant(X3)
model3 = sm.OLS(y, X3).fit()
print(model3.summary())
                            OLS Regression Results                            
==============================================================================
Dep. Variable:                  총범죄건수   R-squared:                       0.903
Model:                            OLS   Adj. R-squared:                  0.876
Method:                 Least Squares   F-statistic:                     33.57
Date:                Fri, 18 Apr 2025   Prob (F-statistic):           1.61e-08
Time:                        01:19:30   Log-Likelihood:                -6.0400
No. Observations:                  24   AIC:                             24.08
Df Residuals:                      18   BIC:                             31.15
Df Model:                           5                                         
Covariance Type:            nonrobust                                         
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
const        1.11e-16      0.073   1.51e-15      1.000      -0.154       0.154
총 음식점 수        0.4745      0.166      2.865      0.010       0.127       0.822
CCTV 수량        0.1789      0.097      1.840      0.082      -0.025       0.383
1인가구수          0.0846      0.098      0.866      0.398      -0.121       0.290
구별 경찰수         0.1832      0.186      0.986      0.337      -0.207       0.574
총생활인구수         0.2817      0.108      2.611      0.018       0.055       0.508
==============================================================================
Omnibus:                        0.922   Durbin-Watson:                   2.122
Prob(Omnibus):                  0.631   Jarque-Bera (JB):                0.287
Skew:                          -0.258   Prob(JB):                        0.867
Kurtosis:                       3.144   Cond. No.                         5.62
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.

회귀분석(상관관계 기준)

회귀분석(Stepwise: AIC)
X1 = X_scaled_df[['총생활인구수', '구별 경찰수','안전벨 개수' ,'CCTV 수량', '총 음식점 수', '1인가구수','파출소수']]
y = X_scaled_df['총범죄건수']

lr = LinearRegression()
names = X1.columns
def aic_score(estimator,X1, y):
    X1 = sm.add_constant(X1) 
    model = sm.OLS(y, X1).fit()
    return -model.aic

# Perform SFS
sfs = SFS(lr,
          k_features=(1,7),   
          forward=True,      
          scoring=aic_score,  
          cv=0)
sfs.fit(X1, y)

print('선택된 features:', np.array(names)[list(sfs.k_feature_idx_)])
print("상관계수 feature: ['총 음식점 수','CCTV 수량','1인가구수','구별 경찰수','총생활인구수']")
선택된 features: ['총생활인구수' '구별 경찰수' 'CCTV 수량' '총 음식점 수' '파출소수']
상관계수 feature: ['총 음식점 수','CCTV 수량','1인가구수','구별 경찰수','총생활인구수']
  • 상관계수 기준으로 선택한 변수와 80% 일치합니다.

회귀분석(Stepwise: AIC)
x1 = X_scaled_df[['총생활인구수', '구별 경찰수', 'CCTV 수량', '총 음식점 수', '파출소수']]
x1 = sm.add_constant(x1)
model1 = sm.OLS(y, x1).fit()
print(model1.summary())
                            OLS Regression Results                            
==============================================================================
Dep. Variable:                  총범죄건수   R-squared:                       0.925
Model:                            OLS   Adj. R-squared:                  0.904
Method:                 Least Squares   F-statistic:                     44.56
Date:                Fri, 18 Apr 2025   Prob (F-statistic):           1.62e-09
Time:                        01:19:32   Log-Likelihood:                -2.9309
No. Observations:                  24   AIC:                             17.86
Df Residuals:                      18   BIC:                             24.93
Df Model:                           5                                         
Covariance Type:            nonrobust                                         
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
const        1.11e-16      0.064   1.72e-15      1.000      -0.135       0.135
총생활인구수         0.2247      0.090      2.484      0.023       0.035       0.415
구별 경찰수         0.4849      0.208      2.328      0.032       0.047       0.922
CCTV 수량        0.1378      0.086      1.609      0.125      -0.042       0.318
총 음식점 수        0.4033      0.149      2.712      0.014       0.091       0.716
파출소수          -0.2671      0.106     -2.509      0.022      -0.491      -0.043
==============================================================================
Omnibus:                        8.993   Durbin-Watson:                   2.176
Prob(Omnibus):                  0.011   Jarque-Bera (JB):                6.911
Skew:                          -1.060   Prob(JB):                       0.0316
Kurtosis:                       4.555   Cond. No.                         6.77
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
  • 아까보다 Adj.R2 도 증가하고, AIC도 감소하고 있습니다.

회귀분석(Stepwise: R2)
X2 = X_scaled_df[['총생활인구수', '구별 경찰수','안전벨 개수' ,'CCTV 수량', '총 음식점 수', '1인가구수','파출소수']]
y = X_scaled_df['총범죄건수']
# Adj R2 스코어 함수 정의
def adjusted_r2_score(estimator, X2, y):
    y_pred = estimator.predict(X2)
    n = X2.shape[0]
    p = X2.shape[1]
    r2 = r2_score(y, y_pred)
    adjusted_r2 = 1 - (1 - r2) * (n - 1) / (n - p - 1)
    return adjusted_r2


sfs = SFS(lr,
          k_features=(1,7),
          forward=True,
          scoring=adjusted_r2_score,
          cv=0)

sfs.fit(X2, y)


selected_indices_r2 = list(sfs.k_feature_idx_)
names_r2 = np.array(X2.columns)[:-1]

x2 = X_scaled_df[['총생활인구수', '구별 경찰수', '안전벨 개수', 'CCTV 수량', '총 음식점 수', '1인가구수']]
x2 = sm.add_constant(x2)
model2 = sm.OLS(y, x2).fit()
print(model2.summary())
  • 마찬가지로, Stepwise 를 사용하여 R2 기준으로 모델을 생성했습니다.

회귀분석(Stepwise: R2)
                            OLS Regression Results                            
==============================================================================
Dep. Variable:                  총범죄건수   R-squared:                       0.905
Model:                            OLS   Adj. R-squared:                  0.872
Method:                 Least Squares   F-statistic:                     27.11
Date:                Fri, 18 Apr 2025   Prob (F-statistic):           8.25e-08
Time:                        01:19:32   Log-Likelihood:                -5.7587
No. Observations:                  24   AIC:                             25.52
Df Residuals:                      17   BIC:                             33.76
Df Model:                           6                                         
Covariance Type:            nonrobust                                         
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
const        1.11e-16      0.075   1.49e-15      1.000      -0.157       0.157
총생활인구수         0.2776      0.110      2.526      0.022       0.046       0.509
구별 경찰수         0.1451      0.198      0.732      0.474      -0.273       0.563
안전벨 개수        -0.0581      0.091     -0.635      0.534      -0.251       0.135
CCTV 수량        0.1921      0.101      1.901      0.074      -0.021       0.405
총 음식점 수        0.5223      0.185      2.831      0.012       0.133       0.912
1인가구수          0.1054      0.105      1.008      0.328      -0.115       0.326
==============================================================================
Omnibus:                        0.222   Durbin-Watson:                   2.051
Prob(Omnibus):                  0.895   Jarque-Bera (JB):                0.228
Skew:                          -0.187   Prob(JB):                        0.892
Kurtosis:                       2.703   Cond. No.                         6.30
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
모델 1의 AIC: 17.862, 모델 1의 R2: 0.925
모델 2의 AIC: 25.517, 모델 2의 R2: 0.905
모델 3의 AIC: 24.080, 모델 3의 R2: 0.903
  • 최종적으로 모델 1을 선택하기로 했습니다.

최종 모델 검증

# 잔차정규성
resid_stats, resid_pvalue = stats.shapiro(residuals)
print(resid_pvalue)
0.07179190990614497
  • 유의수준 5% 기각 X (정규성 만족)

최종 모델 검증

최종 모델 검증
#잔차 등분산성
bptest = het_breuschpagan(model1.resid, model1.model.exog)
print(f'BP-test statistics: {bptest[0]:.3f}')
print(f'BP-test p_value: {bptest[1]:.3f}') 
#p-value가 0.3으로 귀무가설 기각하지 못함
#등분산성 만족한다고 볼 수 있음


#잔차 독립성
dw_stat = durbin_watson(model1.resid)
print(f'DW test: {dw_stat:.3f}')  
# 2 정도로 잔차 독립성 만족한다고 볼 수 있음
BP-test statistics: 5.963
BP-test p_value: 0.310
DW test: 2.176
  • 잔차의 등분산성과 독립성도 만족합니다.

최종 모델 해석
                            OLS Regression Results                            
==============================================================================
Dep. Variable:                  총범죄건수   R-squared:                       0.925
Model:                            OLS   Adj. R-squared:                  0.904
Method:                 Least Squares   F-statistic:                     44.56
Date:                Fri, 18 Apr 2025   Prob (F-statistic):           1.62e-09
Time:                        01:19:33   Log-Likelihood:                -2.9309
No. Observations:                  24   AIC:                             17.86
Df Residuals:                      18   BIC:                             24.93
Df Model:                           5                                         
Covariance Type:            nonrobust                                         
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
const        1.11e-16      0.064   1.72e-15      1.000      -0.135       0.135
총생활인구수         0.2247      0.090      2.484      0.023       0.035       0.415
구별 경찰수         0.4849      0.208      2.328      0.032       0.047       0.922
CCTV 수량        0.1378      0.086      1.609      0.125      -0.042       0.318
총 음식점 수        0.4033      0.149      2.712      0.014       0.091       0.716
파출소수          -0.2671      0.106     -2.509      0.022      -0.491      -0.043
==============================================================================
Omnibus:                        8.993   Durbin-Watson:                   2.176
Prob(Omnibus):                  0.011   Jarque-Bera (JB):                6.911
Skew:                          -1.060   Prob(JB):                       0.0316
Kurtosis:                       4.555   Cond. No.                         6.77
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
  • 선형회귀모델이 통계적으로 유의함을 알 수 있습니다.
  • 또한, CCTV수량을 제외한 변수들은 전부 유의합니다.

서울시 치안 기준 군집화

상관계수가 높은 변수를 활용해서,

비슷한 치안 특성을 가진 구를 찾기위해

저희는 K-Means 클러스터링을 사용했습니다.

K-Means 클러스터링이란?

유사한 특성을 가진 데이터를 자동으로 그룹으로 묶는 비지도 학습 기법

  • 사용자가 군집 개수(K)를 정하면,

  • K개의 중심점을 기준으로 데이터를 그룹화하고,

  • 각 데이터를 가장 가까운 중심점에 할당합니다.

  • 중심점을 반복적으로 조정하며 유사한 데이터끼리 묶는 군집을 만듭니다.

서울시 자치구별 ‘구별_경찰수’, ‘유흥업소_개수’, ‘총생활인구수’, ‘cctv’ 등을 기준으로 치안 특성이 비슷한 지역을 자동으로 군집화하기 위해 사용했습니다.